package com.lcw.library.imagepicker.compressimage;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;


/**
 * Description:图片压缩工具类 author: zhangsan on 16/11/28 上午9:34.
 */
public class CompressImageUtil {

    public static final String TAG = "CompressImageUtil";

    private static final String SDPATH = Environment.getExternalStorageDirectory().getAbsolutePath();
    public static String PATH = SDPATH + File.separator + "daowei/";
    public static String IMAGEPATH = PATH + "image/";
    private static final File mCacheDir = new File(IMAGEPATH);

    private static String DEFAULT_DISK_CACHE_DIR = "compressImgCache";


    public static File thirdCompress(@NonNull File file, String filename) {
        //  String thumb = mCacheDir.getAbsolutePath() + File.separator +
        //        (TextUtils.isEmpty(filename) ? System.currentTimeMillis() : filename) + ".jpg";
        StringBuilder thumb = new StringBuilder(mCacheDir.getAbsolutePath());
        thumb.append(File.separator).append(TextUtils.isEmpty(filename)?System.currentTimeMillis():filename).append(".jpg");
        double size;
        String filePath = file.getAbsolutePath();

        int angle = getImageSpinAngle(filePath);
        int width = getImageSize(filePath)[0];
        int height = getImageSize(filePath)[1];
        int thumbW = width % 2 == 1?width + 1:width;
        int thumbH = height % 2 == 1?height + 1:height;

        width = thumbW > thumbH?thumbH:thumbW;
        height = thumbW > thumbH?thumbW:thumbH;

        double scale = ((double) width / height);

        if (scale <= 1 && scale > 0.5625) {
            if (height < 1664) {
                if (file.length() / 1024 < 150) {
                    return file;
                }

                size = (width * height) / Math.pow(1664, 2) * 150;
                size = size < 60?60:size;
            } else if (height >= 1664 && height < 4990) {
                thumbW = width / 2;
                thumbH = height / 2;
                size = (thumbW * thumbH) / Math.pow(2495, 2) * 300;
                size = size < 60?60:size;
            } else if (height >= 4990 && height < 10240) {
                thumbW = width / 4;
                thumbH = height / 4;
                size = (thumbW * thumbH) / Math.pow(2560, 2) * 300;
                size = size < 100?100:size;
            } else {
                int multiple = height / 1280 == 0?1:height / 1280;
                thumbW = width / multiple;
                thumbH = height / multiple;
                size = (thumbW * thumbH) / Math.pow(2560, 2) * 300;
                size = size < 100?100:size;
            }
        } else if (scale <= 0.5625 && scale > 0.5) {
            if (height < 1280 && file.length() / 1024 < 200) {
                return file;
            }

            int multiple = height / 1280 == 0?1:height / 1280;
            thumbW = width / multiple;
            thumbH = height / multiple;
            size = (thumbW * thumbH) / (1440.0 * 2560.0) * 400;
            size = size < 100?100:size;
        } else {
            int multiple = (int) Math.ceil(height / (1280.0 / scale));
            thumbW = width / multiple;
            thumbH = height / multiple;
            size = ((thumbW * thumbH) / (1280.0 * (1280 / scale))) * 500;
            size = size < 100?100:size;
        }

        return compress(filePath, thumb.toString(), thumbW, thumbH, angle, (long) size);
    }

    public static File firstCompress(@NonNull File file, String filename) {
        int minSize = 60;
        int longSide = 720;
        int shortSide = 1280;

        String filePath = file.getAbsolutePath();
        String thumbFilePath = mCacheDir.getAbsolutePath() + File.separator + (TextUtils.isEmpty(filename)?System.currentTimeMillis():filename) + ".jpg";

        long size = 0;
        long maxSize = file.length() / 5;

        int angle = getImageSpinAngle(filePath);
        int[] imgSize = getImageSize(filePath);
        int width = 0, height = 0;
        if (imgSize[0] <= imgSize[1]) {
            double scale = (double) imgSize[0] / (double) imgSize[1];
            if (scale <= 1.0 && scale > 0.5625) {
                width = imgSize[0] > shortSide?shortSide:imgSize[0];
                height = width * imgSize[1] / imgSize[0];
                size = minSize;
            } else if (scale <= 0.5625) {
                height = imgSize[1] > longSide?longSide:imgSize[1];
                width = height * imgSize[0] / imgSize[1];
                size = maxSize;
            }
        } else {
            double scale = (double) imgSize[1] / (double) imgSize[0];
            if (scale <= 1.0 && scale > 0.5625) {
                height = imgSize[1] > shortSide?shortSide:imgSize[1];
                width = height * imgSize[0] / imgSize[1];
                size = minSize;
            } else if (scale <= 0.5625) {
                width = imgSize[0] > longSide?longSide:imgSize[0];
                height = width * imgSize[1] / imgSize[0];
                size = maxSize;
            }
        }

        return compress(filePath, thumbFilePath, width, height, angle, size);
    }

    /**
     * obtain the image's width and height
     * @param imagePath
     *         the path of image
     */
    public static int[] getImageSize(String imagePath) {
        int[] res = new int[2];

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        options.inSampleSize = 1;
        BitmapFactory.decodeFile(imagePath, options);

        res[0] = options.outWidth;
        res[1] = options.outHeight;

        return res;
    }

    /**
     * obtain the thumbnail that specify the size
     * @param imagePath
     *         the target image path
     * @param width
     *         the width of thumbnail
     * @param height
     *         the height of thumbnail
     * @return {@link Bitmap}
     */
    private static Bitmap compress(String imagePath, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imagePath, options);

        int outH = options.outHeight;
        int outW = options.outWidth;
        int inSampleSize = 1;

        if (outH > height || outW > width) {
            int halfH = outH / 2;
            int halfW = outW / 2;

            while ((halfH / inSampleSize) > height && (halfW / inSampleSize) > width) {
                inSampleSize *= 2;
            }
        }

        options.inSampleSize = inSampleSize;

        options.inJustDecodeBounds = false;

        int heightRatio = (int) Math.ceil(options.outHeight / (float) height);
        int widthRatio = (int) Math.ceil(options.outWidth / (float) width);

        if (heightRatio > 1 || widthRatio > 1) {
            if (heightRatio > widthRatio) {
                options.inSampleSize = heightRatio;
            } else {
                options.inSampleSize = widthRatio;
            }
        }
        options.inJustDecodeBounds = false;

        return BitmapFactory.decodeFile(imagePath, options);
    }

    /**
     * obtain the image rotation angle
     * @param path
     *         path of target image
     */
    private static int getImageSpinAngle(String path) {
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }

    /**
     * 指定参数压缩图片 create the thumbnail with the true rotate angle
     * @param largeImagePath
     *         the big image path
     * @param thumbFilePath
     *         the thumbnail path
     * @param width
     *         width of thumbnail
     * @param height
     *         height of thumbnail
     * @param angle
     *         rotation angle of thumbnail
     * @param size
     *         the file size of image
     */
    private static File compress(String largeImagePath, String thumbFilePath, int width, int height, int angle, long size) {
        Bitmap thbBitmap = compress(largeImagePath, width, height);

        thbBitmap = rotatingImage(angle, thbBitmap);

        return saveImage(thumbFilePath, thbBitmap, size);
    }

    /**
     * 旋转图片 rotate the image with specified angle
     * @param angle
     *         the angle will be rotating 旋转的角度
     * @param bitmap
     *         target image               目标图片
     */
    private static Bitmap rotatingImage(int angle, Bitmap bitmap) {
        //rotate image
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);

        //create a new image
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

    public static <T> T checkNotNull(T reference, @Nullable Object errorMessage) {
        if (reference == null) {
            throw new NullPointerException(String.valueOf(errorMessage));
        }
        return reference;
    }

    /**
     * 保存图片到指定路径 Save image with specified size
     * @param filePath
     *         the image file save path 储存路径
     * @param bitmap
     *         the image what be save   目标图片
     * @param size
     *         the file size of image   期望大小
     */
    private static File saveImage(String filePath, Bitmap bitmap, long size) {
        checkNotNull(bitmap, TAG + "bitmap cannot be null");

        File result = new File(filePath.substring(0, filePath.lastIndexOf("/")));

        if (!result.exists() && !result.mkdirs()) {
            return null;
        }

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        int options = 100;
        bitmap.compress(Bitmap.CompressFormat.JPEG, options, stream);

        while (stream.toByteArray().length / 1024 > size && options > 6) {
            stream.reset();
            options -= 6;
            bitmap.compress(Bitmap.CompressFormat.JPEG, options, stream);
        }
        bitmap.recycle();

        try {
            FileOutputStream fos = new FileOutputStream(filePath);
            fos.write(stream.toByteArray());
            fos.flush();
            fos.close();
            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return new File(filePath);
    }

    /**
     * Returns a directory with a default name in the private cache directory of the application to use to store retrieved media and thumbnails.
     * @param context
     *         A context.
     * @see #getPhotoCacheDir(Context, String)
     */
    private static synchronized File getPhotoCacheDir(Context context) {
        return getPhotoCacheDir(context, DEFAULT_DISK_CACHE_DIR);
    }

    /**
     * Returns a directory with the given name in the private cache directory of the application to use to store retrieved media and thumbnails.
     * @param context
     *         A context.
     * @param cacheName
     *         The name of the subdirectory in which to store the cache.
     * @see #getPhotoCacheDir(Context)
     */
    private static File getPhotoCacheDir(Context context, String cacheName) {
        File cacheDir = context.getCacheDir();
        if (cacheDir != null) {
            File result = new File(cacheDir, cacheName);
            if (!result.mkdirs() && (!result.exists() || !result.isDirectory())) {
                // File wasn't able to create a directory, or the result exists but not a directory
                return null;
            }

            File noMedia = new File(cacheDir + "/.nomedia");
            if (!noMedia.mkdirs() && (!noMedia.exists() || !noMedia.isDirectory())) {
                return null;
            }

            return result;
        }

        Log.e(TAG, "default disk cache dir is null");

        return null;
    }


    private static String getImageType(String filePath) {

        // File file = new File("C:/Users/Administrator/Desktop/111.png");
        // File file = new File("C:/Users/Administrator/Desktop/111.jpg");
        File file = new File(filePath);
        if (!file.exists()) {
            System.err.println("--- file not exists! ---");
            return null;
        }

        byte[] png_type = new byte[]{(byte) 0x89, (byte) 0x50, (byte) 0x4E, (byte) 0x47, (byte) 0x0D, (byte) 0x0A, (byte) 0x1A, (byte) 0x0A, '\0'};
        byte file_head[] = new byte[9];

        try {
            FileInputStream fis = new FileInputStream(file);
            fis.read(file_head, 0, 9);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        file_head[8] = '\0';
        switch (file_head[0]) {
            case (byte) 0xff:
                if (file_head[1] == (byte) 0xd8) {
                    return "jpg";// jpeg
                }
            case (byte) 0x42:
                if (file_head[1] == (byte) 0x4D) {
                    return "bmp";// bmp
                }
            case (byte) 0x89:
                if (file_head[1] == png_type[1] && file_head[2] == png_type[2] && file_head[3] == png_type[3] && file_head[4] == png_type[4] && file_head[5] == png_type[5] && file_head[6] == png_type[6] && file_head[7] == png_type[7]) {
                    return "png";// png
                }
        }

        return null;
    }
}
